﻿#include "mainwindow.h"
#include "ui_mainwindow.h"

QSerialPort myserial;
//uint8 frame_buffer[300];
QTimer *timer_serial_rece_timeout;//串口接收断帧，定时器
int frameInterTime = 50;//串口接收断帧时长
QTimer *timer_recycle_send;//循环发送，定时器

QByteArray rece_frame;

QSqlTableModel_My *model_645_did;
Dlg_SelectDid *dlg_select_did;

QSqlTableModel_My *model_7E_did;
Dlg_Select_7E_Did *dlg_select_7E_did;

QSqlTableModel_My *model_7E_RemoteCMD;
Dlg_Preserve_Remote_CMD *dlg_preserve_remote_cmd;

QSqlTableModel_My *model_7E_LocalCMD;
Dlg_Preserve_Local_CMD *dlg_preserve_local_cmd;

QSqlTableModel_My *model_645_FuncCode;
Dlg_preserve_645_func_code *dlg_preserve_645_func_code;

QSqlTableModel_My *model_common_7E;
Dlg_common_frame *dlg_common_frame;

Dlg_Setting *dlg_settings;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setWindowIcon(QIcon(":/image/7E645.ico"));

    dlg_settings = new Dlg_Setting(this);
    QObject::connect(dlg_settings,SIGNAL(send_settings(struct Settings)),this,SLOT(on_ReceSettings(struct Settings)));

    QObject::connect(&myserial,SIGNAL(error(QSerialPort::SerialPortError)),this,SLOT(on_SerialPortError_occurred(QSerialPort::SerialPortError)));//串口错误处理
    QObject::connect(&myserial,&QSerialPort::readyRead,this,&MainWindow::on_serial_receive_data);//接收中断
    timer_serial_rece_timeout = new QTimer(this);
    QObject::connect(timer_serial_rece_timeout,SIGNAL(timeout()),this,SLOT(on_timer_serial_rece_timeout()));//接收超时
    timer_recycle_send = new QTimer(this);
    QObject::connect(timer_recycle_send,SIGNAL(timeout()),this,SLOT(on_timer_recycle_send_timeout()));
    timer_recycle_send->setSingleShot(true);

    menu=new QMenu(this);
    Act_settings = new QAction("设置",this);
    menu->addAction(Act_settings); //添加菜单项
    QObject::connect(Act_settings,SIGNAL(triggered()),this,SLOT(on_Act_settings_triggered()));
    Act_about = new QAction("关于本软件",this);
    QObject::connect(Act_about,SIGNAL(triggered()),this,SLOT(on_Act_about_triggered()));
    menu->addAction(Act_about); //添加菜单项

    MainWindow::installEventFilter(this);//安装事件过滤器
//ui->statusBar->setStatusTip();
    //ui->labelGidType->setIndent(2);
    //ui->textEditRece->setp

    sql_load_and_init();//载入数据库内容，并初始化对应的窗口表格

    ui->comboBoxDataBits->setCurrentText("8");
    ui->comboBoxBaud->setCurrentText("9600");
    ui->comboBoxLocalBand->setCurrentText("9600");
    ui->comboBoxGidType->setCurrentText("1->单字节地址");

    ui->lineEditSaid->setValidator(new QRegExpValidator(QRegExp("[0-9]{0,10}"),this));
    ui->lineEditPanid->setValidator(new QIntValidator(0,65535,this));
    ui->lineEditTaid->setValidator(new QRegExpValidator(QRegExp("[0-9]{0,10}"),this));
    ui->lineEditSid->setValidator(new QRegExpValidator(QRegExp("[ 0-9A-Fa-f]*"),this));
    ui->lineEditDID->setValidator(new QRegExpValidator(QRegExp("[0-9A-Fa-f]{0,4}"),this));
    ui->lineEdit645DID->setValidator(new QRegExpValidator(QRegExp("[0-9A-Fa-f]{0,8}"),this));
    ui->lineEdit645DID->setDidType(DidType_645);
    ui->lineEditData->setValidator(new QRegExpValidator(QRegExp("[ 0-9A-Fa-f]*"),this));
    ui->lineEdit645CustomData->setValidator(new QRegExpValidator(QRegExp("[ 0-9A-Fa-f]*"),this));
    ui->tableWidgetDisp->horizontalHeader()->setSectionResizeMode(0,QHeaderView::ResizeToContents);//某列宽自适应内容
    ui->tableWidgetDisp->horizontalHeader()->setSectionResizeMode(1,QHeaderView::ResizeToContents);//某列宽自适应内容
    ui->tableWidgetDisp->horizontalHeader()->setSectionResizeMode(2,QHeaderView::ResizeToContents);//某列宽自适应内容
    ui->tableWidgetDisp->horizontalHeader()->setStretchLastSection(true);//最后一列自动扩展
    ui->lineEditData->setPlaceholderText("每个字节必须为两个hex字符，例如：5A F2 47");
    ui->lineEdit645CustomData->setPlaceholderText("输入内容应与报文的字节顺序相反");
    ui->comboBoxCMD->view()->setFixedWidth(180);//下拉列表条目宽度>显示宽度
    ui->comboBoxFlow->view()->setFixedWidth(120);//下拉列表条目宽度>显示宽度
    //ui->comboBoxCMD645->view()->setFixedWidth(150);//下拉列表条目宽度>显示宽度
    ui->comboBoxGidType->view()->setFixedWidth(100);//下拉列表条目宽度>显示宽度
    ui->comboBox_portName->view()->setFixedWidth(500);//下拉列表条目宽度>显示宽度
    //static QRect Rect_tmp(0,0,200,200);
    //ui->comboBox_portName->view()->setFrameShape(QFrame::HLine);
    //ui->comboBox_portName->view()->setFrameRect();


    ui->checkBoxFoldSerial->setChecked(true);//翻转一次。否则动画卡顿
    ui->checkBoxFoldSerial->setChecked(false);

    ui->comboBoxCMD645->clear();//
    for(uint8 row=0;row<model_645_FuncCode->rowCount();row++ )
    {
        QString dbFuncCode_str = model_645_FuncCode->record(row).value("funcCode").toString();
        QString instruction_str = model_645_FuncCode->record(row).value("instruction").toString();
        ui->comboBoxCMD645->addItem(dbFuncCode_str + "-" + instruction_str);
    }


    for(uint8 idx=0;idx<sizeof(frameBoxBody)/sizeof(frameBoxBody[0]);idx++ )
    {        
        frameBoxBody[idx] = new FrameBoxBody(ui->frameBoxBody_0);
        frameBoxBody[idx]->setMaximumWidth(0);
        ui->gridLayoutMultiDid->addWidget(frameBoxBody[idx], 0, 3+idx, 2, 1);
        static quint16 did_u16=0x0003;
        frameBoxBody[idx]->lineEditDID->setText(QString("%1").arg(did_u16,4,16,(QChar)'0'));
        did_u16 += 2;
    }
    ui->spinBoxMultiDid->setMaximum(5);
    ui->spinBoxMultiDid->setMinimum(1);
    ui->spinBoxMultiDid->setValue(1);
    //on_spinBoxMultiDid_valueChanged(ui->spinBoxMultiDid->value());

    labelPush = new QLabel(this);
    labelPush->setGeometry(0,0,100,100);
    labelPush->hide();
    QPixmap *pic =  new QPixmap(":/image/push.png");
    labelPush->setPixmap(*pic);
    labelPush->setScaledContents(true);

//    QMovie *movie = new QMovie(":/image/pushGif.gif");
//    labelPush->setMovie(movie);
//    movie->start();
//    labelPush->show();




    on_pushButtonScan_clicked();
    init_animation();

    ini_save_or_restore(false);



//    uint8 u8_arr[3]={0xF5,0x07,0xa5};
//    QByteArray qa2(u8_arr,3);
//    QString qstr(qa2.toHex());
//    qDebug()<<qstr.toUpper();//输出字符串：F507A5




}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::contextMenuEvent(QContextMenuEvent *)
{
    QCursor cur=this->cursor();

    menu->exec(cur.pos()); //关联到光标
    qDebug()<<"右键触发";

}
void MainWindow::on_Act_settings_triggered()
{
    dlg_settings->exec();
}
void MainWindow::on_Act_about_triggered()
{
    QMessageBox::about(this,"关于本软件：",
                      QString("本软件支持的功能有：：\r\n")+
                      QString("1、本地报文的组建&解析；\r\n")+
                      QString("2、远程报文（单点/广播）的组建&解析；\r\n")+
                      QString("3、支持组合报文发送&解析；\r\n")+
                      QString("4、支持单独的07645或者把用7E包645，组建&解析；\r\n")+
                      QString("5、对于新增的不支持解析的本地/远程CMD、did等，在相应的数据维护界面添加上，即可支持；\r\n\r\n\r\n")+
                      QString("----------------------------------------------2018.3.29 gyh 创建")
                      );
   qDebug()<<"右键选中";
   //ui->lineEditDID_2
}
void MainWindow::sql_load_and_init()
{
    if ( !createConnection() )
        qDebug()<<"数据库连接->失败";
    else
        qDebug()<<"数据库连接->成功";

    model_645_did = new QSqlTableModel_My;
    model_645_did->setTable("table_645_did");//model绑定到db库的TableDid表
    model_645_did->setEditStrategy(QSqlTableModel::OnFieldChange);//设定何时将更改更新到数据库？

    model_645_did->select();

    dlg_select_did = new Dlg_SelectDid(this);
    QObject::connect(dlg_select_did,SIGNAL(send_select(QString)),this,SLOT( on_DidSelect(QString) ));

    model_7E_did = new QSqlTableModel_My;
    model_7E_did->setTable("table_7E_did");//model绑定到db库的TableDid表
    model_7E_did->setEditStrategy(QSqlTableModel::OnFieldChange);//设定何时将更改更新到数据库？
    model_7E_did->setSort(0,Qt::AscendingOrder);
    model_7E_did->select();

    dlg_select_7E_did = new Dlg_Select_7E_Did(this);
    QObject::connect(dlg_select_7E_did,SIGNAL(send_select(QString)),this,SLOT( on_7E_DidSelect(QString) ));


    model_645_FuncCode = new QSqlTableModel_My;
    model_645_FuncCode->setTable("table_func_code_645");//model绑定到db库的model_645_FuncCode表
    model_645_FuncCode->setEditStrategy(QSqlTableModel::OnFieldChange);//设定何时将更改更新到数据库？
    model_645_FuncCode->select();
    dlg_preserve_645_func_code = new Dlg_preserve_645_func_code(this);

    model_7E_RemoteCMD = new QSqlTableModel_My;
    model_7E_RemoteCMD->setTable("table_remote_cmd");//model绑定到db库的table_remote_cmd表
    model_7E_RemoteCMD->setEditStrategy(QSqlTableModel::OnFieldChange);//设定何时将更改更新到数据库？
    model_7E_RemoteCMD->select();
    dlg_preserve_remote_cmd = new Dlg_Preserve_Remote_CMD(this);
    ui->comboBoxCMD->clear();
    QSqlRecord record;//获取model某行的所有内容
    for(int row=0;row<model_7E_RemoteCMD->rowCount();row++ )
    {
        record = model_7E_RemoteCMD->record(row);
        QString cmd_str = record.value("cmd").toString();
        QString instruction_str = record.value("instruction").toString();
        ui->comboBoxCMD->addItem(cmd_str + "-" + instruction_str );
    }


    model_7E_LocalCMD = new QSqlTableModel_My;
    model_7E_LocalCMD->setTable("table_local_cmd");//model绑定到db库的table_local_cmd表
    model_7E_LocalCMD->setEditStrategy(QSqlTableModel::OnFieldChange);//设定何时将更改更新到数据库？
    model_7E_LocalCMD->select();
    dlg_preserve_local_cmd = new Dlg_Preserve_Local_CMD(this);    
    QObject::connect(dlg_preserve_local_cmd,SIGNAL(send_local_frame(const char *, qint64)),this,SLOT( on_loacl_frame_encoded(const char *, qint64) ));


    model_common_7E = new QSqlTableModel_My;
    model_common_7E->setTable("table_common_7E");//model绑定到db库的table_common_7E表
    model_common_7E->setEditStrategy(QSqlTableModel::OnFieldChange);//设定何时将更改更新到数据库？
    model_common_7E->select();
    dlg_common_frame = new Dlg_common_frame(this);


    int columun_idx = model_common_7E->fieldIndex("instruction");
    for(int db_row=0;db_row < model_common_7E->rowCount();db_row++)
    {
        QModelIndex index = model_common_7E->index(db_row,columun_idx);
        QString item_str = model_common_7E->data(index).toString();
        ui->comboBoxTestExample->addItem(item_str);
    }



}
void MainWindow::init_animation()
{


}


/*保存信息到ini文件，或者从ini文件恢复信息
 * 参数isSave=true为保存，否则为恢复
 *ini格式分为：段、key、值，例如：configIniWrite->setValue("ip/first", "192.168.0.1");
 * 段为ip，key为first，192.168.0.1为值
 * */
void MainWindow::ini_save_or_restore(bool isSave)
{

    QSettings *IniFile = new QSettings("meter.ini", QSettings::IniFormat);
    IniFile->setIniCodec("UTF-8"); ;
    if(isSave == true)
    {
        ui->checkBoxBroadcast->setChecked(false);
        QString taid_str = ui->lineEditTaid->text();
        QString enable645_str = ui->checkBoxUse645->isChecked()?"yes":"no";
        QString Fold_local_str =ui->checkBoxFoldLocal->isChecked()?"yes":"no";
        QString frame_interval_str = ui->spinBoxFrameInterval->text();

        QString win_x_str = QString("%1").arg(this->geometry().x());
        QString win_y_str = QString("%1").arg(this->geometry().y());
        QString win_wideth_str = QString("%1").arg(this->geometry().width());
        QString win_height_str = QString("%1").arg(this->geometry().height());


        IniFile->setValue("aid/taid", taid_str);
        IniFile->setValue("checkBox/enable645", enable645_str);
        IniFile->setValue("checkBox/Fold_local", Fold_local_str);
        IniFile->setValue("set/frame_interval", frame_interval_str);

        IniFile->setValue("win_geometry/x", win_x_str);
        IniFile->setValue("win_geometry/y", win_y_str);
        IniFile->setValue("win_geometry/wideth", win_wideth_str);
        IniFile->setValue("win_geometry/height", win_height_str);

        IniFile->setValue("serial/name", ui->comboBox_portName->currentText());
        IniFile->setValue("serial/Baud", ui->comboBoxBaud->currentText());
        IniFile->setValue("serial/DataBits", ui->comboBoxDataBits->currentText());
        IniFile->setValue("serial/StopBits", ui->comboBoxStopBits->currentText());
        IniFile->setValue("serial/Parity", ui->comboBoxParity->currentText());

        IniFile->setValue("setting/keyDidAnaly", QString("%1").arg(settings.key_showDidAnaly) );

        qDebug()<<"配置信息已保存";
    }
    else
    {
        QString read_taid_str = IniFile->value("aid/taid",(QVariant)1).toString();
        ui->lineEditTaid->setText(read_taid_str);
        QString read_enable645 = IniFile->value("checkBox/enable645",(QVariant)"yes").toString();
        if(read_enable645 == "yes")
        {
            ui->checkBoxUse645->setChecked(false);
            ui->checkBoxUse645->setChecked(true);
        }
        else
        {
            ui->checkBoxUse645->setChecked(true);
            ui->checkBoxUse645->setChecked(false);
        }        

        int frame_interval = IniFile->value("set/frame_interval",(QVariant)"50").toInt();
        ui->spinBoxFrameInterval->setValue(frame_interval);



        QString Fold_local_str = IniFile->value("checkBox/Fold_local",(QVariant)"no").toString();
        if(Fold_local_str == "yes")
        {
            ui->checkBoxFoldLocal->setChecked(false);
            ui->checkBoxFoldLocal->setChecked(true);
        }
        else
        {
            ui->checkBoxFoldLocal->setChecked(true);
            ui->checkBoxFoldLocal->setChecked(false);
        }


        QString win_x_str = IniFile->value("win_geometry/x",(QVariant)0).toString();
        QString win_y_str = IniFile->value("win_geometry/y",(QVariant)0).toString();
        QString win_wideth_str = IniFile->value("win_geometry/wideth",(QVariant)1000).toString();
        QString win_height_str = IniFile->value("win_geometry/height",(QVariant)600).toString();
        QRect win_rect = this->geometry();
        win_rect.setX( win_x_str.toInt()  );
        win_rect.setY( win_y_str.toInt()  );
        win_rect.setWidth( win_wideth_str.toInt()  );
        win_rect.setHeight( win_height_str.toInt()  );
        this->setGeometry(win_rect);

        QString serial_name_str = IniFile->value("serial/name",(QVariant)"").toString();
        QString serial_band_str = IniFile->value("serial/Baud",(QVariant)"9600").toString();
        QString serial_DataBits_str = IniFile->value("serial/DataBits",(QVariant)"8").toString();
        QString serial_StopBits_str = IniFile->value("serial/StopBits",(QVariant)"1").toString();
        QString serial_Parity_str = IniFile->value("serial/Parity",(QVariant)"无").toString();

        ui->comboBox_portName->setCurrentText(serial_name_str);
        ui->comboBoxBaud->setCurrentText(serial_band_str);
        ui->comboBoxDataBits->setCurrentText(serial_DataBits_str);
        ui->comboBoxStopBits->setCurrentText(serial_StopBits_str);
        ui->comboBoxParity->setCurrentText(serial_Parity_str);
        //ui->frameBoxGroup        

        settings.key_showDidAnaly = IniFile->value("setting/keyDidAnaly",(QVariant)"0").toInt();

        qDebug()<<"配置信息已读出";
    }

    delete IniFile;

}

bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
    //需在构造函数中执行QObject::installEventFilter(this);//安装过滤器
    //本函数才能生效
    //qDebug()<<"主窗体事件：obj:"<<obj->objectName()<<" 类型:"<<event->type();

    if( obj == this )//事件来源:本窗口
    {
        //窗口停用，变为不活动的窗口
        if(QEvent::WindowDeactivate == event->type())//事件类型:丧失激活状态
        {
            qDebug()<<"丧失激活";

            return true ;
        }
        else
        {
            return false ;
        }
    }
    return false ;
}
void MainWindow::closeEvent(QCloseEvent *event)
{
    //QMessageBox::information(this, tr("提示"), tr("真的关闭？"));
    ini_save_or_restore(true);
    event->accept();

}

void MainWindow::on_serial_receive_data()
{
    QByteArray dataArray;
    dataArray = myserial.readAll();//读取数据
    rece_frame += dataArray;//rece_frame收完一帧数据后会被清空
    int bytesCnt = ui->labelReceByteCnt->text().toInt();
    ui->labelReceByteCnt->setText(QString::number(bytesCnt+dataArray.count()));

    //ui->textEditRece->insertPlainText(dataArray.toHex().toUpper()+" ");
    QString frame_tmp_str = byte_num_to_hex_string_with_space(dataArray);

    qDebug()<<"中断字节串:"<<frame_tmp_str;
    ui->textEditRece->insertPlainText(frame_tmp_str + " ");


    QTextCursor cursor=ui->textEditRece->textCursor();//保持编辑器在光标最后一行
    cursor.movePosition(QTextCursor::End); //保持编辑器在光标最后一行
    ui->textEditRece->setTextCursor(cursor);//保持编辑器在光标最后一行
    timer_serial_rece_timeout->start(frameInterTime);//默认20ms断帧
}
void MainWindow::on_SerialPortError_occurred(QSerialPort::SerialPortError error)
{
    if(QSerialPort::NoError == error) return;

    QString error_infor;
    switch(error)
    {
    case QSerialPort::DeviceNotFoundError:error_infor = "未找到该串口，无法打开！";break;
    case QSerialPort::PermissionError:error_infor = "无法开启该串口，可能已被打开或已被别的软件占用";break;
    case QSerialPort::OpenError:error_infor = "无法开启该串口，可能已被打开或已被别的软件占用";break;
    case QSerialPort::NotOpenError:error_infor = "该操作只能在串口开启时才能执行";break;
    case QSerialPort::ParityError:error_infor = "串口收到的校验位有误";break;
    //case QSerialPort::FramingError:
    //case QSerialPort::BreakConditionError:
    case QSerialPort::WriteError:error_infor = "发送失败，可能串口已掉线";break;
    case QSerialPort::ReadError:error_infor = "无法读取串口数据！";break;
    case QSerialPort::ResourceError:error_infor = "可能串口已被拔掉，请重新扫描串口！";break;
    //case QSerialPort::UnsupportedOperationError:
    //case QSerialPort::TimeoutError:
    default:error_infor = "串口发生未知错误，请检查";break;
    }
    qDebug()<<"串口错误:"<<error;

    if( myserial.isOpen() )
        myserial.close();

    ui->pushButtonOpenSerial->setIcon(QIcon(":/image/serial_closed_icon"));
    ui->pushButtonOpenSerial->setText("打开串口");

    QMessageBox::information(this, tr("串口发生错误，请重新开启串口试试"), error_infor);

}
void MainWindow::serial_ui_init()
{

}
/*功能：组建body域  did ctrl data
 * 返回：组建好的多个body域的总长，若ui的输入值有误，则返回负数
 * */
int MainWindow::encodeFrameBody(struct FBD_body *frame_body)
{
    int body_total_len=0;

    //组建did
    int did_len = hex_str_to_byte_arr(ui->lineEditDID->text(),frame_body->did, 400, false);
    if(did_len!=2)
    {
        qDebug("7E->did转换失败，错误号=%d",did_len);
        return -1;
    }
    //组建data
    int data_len = hex_str_to_byte_arr(ui->lineEditData->text(),frame_body->data, 400,true);
    if(data_len<0)
    {
        qDebug("7E->infor->data转换失败，错误号=%d",data_len);
        return -2;
    }
    //组建ctrl
    frame_body->ctrl = data_len;

    body_total_len = DID_LEN + 1 +data_len;//

    frame_body = (struct FBD_body *)((uint8 *)frame_body + DID_LEN + 1 +data_len);

    for(int idx =0; idx<ui->spinBoxMultiDid->value()-1;idx++ )
    {
        //组建did
        did_len = hex_str_to_byte_arr(frameBoxBody[idx]->lineEditDID->text(),frame_body->did,400, false);
        //qDebug()<<"did_str:"<<frameBoxBody[idx]->lineEditDID->text();
        //qDebug("body->did[%d] len =%d arr=", idx, did_len);
        //print_arr(frame_body->did, did_len);

        if(did_len!=2)
        {
            qDebug("7E->did转换失败，错误号=%d",did_len);
            return -1;
        }
        //组建data
        data_len = hex_str_to_byte_arr(frameBoxBody[idx]->lineEditData->text(),frame_body->data, 400, true);
        //qDebug()<<"data_str:"<<frameBoxBody[idx]->lineEditData->text();
        //qDebug("body->data[%d] len =%d arr=", idx, data_len);
        //print_arr(frame_body->data, data_len);
        if(data_len<0)
        {
            qDebug("7E->infor->data转换失败，错误号=%d",data_len);
            return -2;
        }
        //组建ctrl
        frame_body->ctrl = data_len;

        frame_body = (struct FBD_body *)((uint8 *)frame_body + DID_LEN + 1 +data_len);
        body_total_len += DID_LEN + 1 +data_len;
    }


    return body_total_len;
}


/*
 * 功能：根据GUI的设置组建7E报文，
 * 返回：组建好的7E报文的静态数组首地址，若GUI的设置不合法，则返回NULL *
 *
 * 广播C012继电器动作：       |seq|len|cmd|  gid  |did |ctrl|data| CS
7E 01 00 00 00  FF FF FF FF  01  07  07  41 00  12 C0 01  82  xx
 */
struct SHS_frame * MainWindow::encodeFrame7E_FromGui()
{
    bool ok;
    static uint8 frame_buffer[500];
    struct SHS_frame *pframe = (struct SHS_frame*)frame_buffer;
    //报文头
    pframe->stc = 0x7E;
    //组建源地址、目的地址、帧序号
    //*(quint32 *)pframe->said  = ui->lineEditSaid->text().toInt();
    put_le_val(ui->lineEditSaid->text().toInt(), pframe->said, 4);
    if(true == ui->checkBoxBroadcast->isChecked())
        memset(pframe->taid, 0xFF, 4);
    else
        //*(quint32 *)pframe->taid  = ui->lineEditTaid->text().toInt();
        put_le_val(ui->lineEditTaid->text().toInt(), pframe->taid, 4);

    //组建帧序号
    static uint8 seq_tmp=0;
    pframe->seq = seq_tmp++;

    //组建CMD
    uint8 *cmd_tmp = pframe->infor;
    *cmd_tmp = ui->comboBoxCMD->currentText().left(2).toInt(&ok,16);


    //组建gid（如果是广播）（gid=type+sid）
    int gid_len;
    if(true == ui->checkBoxBroadcast->isChecked())
    {
        uint8 *gid = cmd_tmp + 1;
        uint8 *sid = gid + 1;
        //组建SID
        int sid_len = hex_str_to_byte_arr(ui->lineEditSid->text(),sid,sizeof(frame_buffer)-20 ,true);
        if(sid_len<0)
        {
            qDebug("7E->gid->sid转换失败，错误号=%d",sid_len);
            return NULL;
        }
        //组建GID
        uint8 gid_type = ui->comboBoxGidType->currentText().left(1).toInt();
        *gid = (gid_type<<6) + (uint8)sid_len;
        gid_len = 1 + sid_len;
    }
    else
        gid_len=0;

    //组建body（广播和单点，body在报文中的位置不同）
    uint8 *body;//body=did+ctrl+data
    if(true == ui->checkBoxBroadcast->isChecked())
        body = pframe->infor + CMD_LEN + gid_len;
    else
        body = pframe->infor + CMD_LEN;

    struct FBD_body *frame_body = (struct FBD_body *)body;
    int body_total_len = encodeFrameBody(frame_body);
    if(body_total_len<0)
        return NULL;


    //组建pframe->length
    pframe->length = CMD_LEN + gid_len + body_total_len;//CMD+GID+N*body(DID+CTRL+DATA)(非广播则gid_len=0)
    //sum
    pframe->infor[pframe->length] = checksum(frame_buffer, SHS_FRAME_HEAD+pframe->length);

    return pframe;
}
struct frame07645 * MainWindow::encodeFrame645_FromGui()
{
    bool ok;
    static char frame645[100];
    struct frame07645 *pframe = (struct frame07645 *)frame645;
    pframe->st1 = 0x68;
    pframe->st2 = 0x68;

    //下面组建表地址，把gui上的十进制数转成frame645数组的bcd
    QString addr_str = ui->lineEditMeterAddr->text();
    if( addr_str== "AAAAAAAAAAAA")
    {
        memset(pframe->meter_addr,0xAA,6);
    }
    else if(addr_str== "999999999999")//99
    {
        memset(pframe->meter_addr,0x99,6);
    }
    else//normal,十进制转bcd
    {
        quint32 meter_addr_num=ui->lineEditMeterAddr->text().toULong(&ok,10);
        hex2bcd_le(meter_addr_num, pframe->meter_addr, 6);
    }
    //组织控制码
    QString cmd_str = ui->comboBoxCMD645->currentText();
    //cmd_str.replace(QRegExp("[^0-9a-fA-F]"), "");//删除非HEX字符
    pframe->control_byte = cmd_str.left(2).toInt(&ok, 16);
    if(!ok)
    {
        qDebug()<<"提取控制码出错";
        QMessageBox::information(this, tr("错误提示"), "无法识别界面上645的控制码");
    }
    else
        qDebug("control_byte->%02X",pframe->control_byte);

    //下面组织pframe->data_len和645报文中pframe->data

    //pframe->function_flag//首先根据功能码定位到数据库表指定的行
    bool isOK;
    int db_row=0;
    for(db_row=0;db_row < model_645_FuncCode->rowCount();db_row++)
    {
        QString dbFuncCode_str = model_645_FuncCode->record(db_row).value("funcCode").toString().left(2);
        uint8 dbFuncCode = dbFuncCode_str.toInt(&isOK,16);
        if( pframe->function_flag ==  dbFuncCode)
        {
            break;
        }
    }
    if(db_row == model_645_FuncCode->rowCount() )
    {
        QMessageBox::information(this, tr("功能提示"), "目前不支持该功能码，在功能码维护界面添加后即可支持");
        return NULL;
    }
    //在数据库中查找功能码已完成

    //组建did(如果该功能码需要did的话)
    int data_len=0;
    QString containDid_str = model_645_FuncCode->record(db_row).value("ContainDid").toString();
    qDebug()<<"数据库中ContainDid的内容是->"<<containDid_str;
    if(containDid_str == "是")
    {
        QString did_str=ui->lineEdit645DID->text();
        int did_len = hex_str_to_byte_arr(did_str, pframe->data, sizeof(frame645)-20,false);//DID
        if(did_len != 4)
        {
            QMessageBox::information(this, tr("错误提示"), "输入的645->did有误");
            return NULL;
        }
        data_len += 4;
    }
    //组建密码和操作者代码(如果该功能码需要密码和操作者代码的话)
    QString containPW_OP_str = model_645_FuncCode->record(db_row).value("ContainPW_OP").toString();
    qDebug()<<"数据库中containPW_OP_str的内容是->"<<containPW_OP_str;
    if(containPW_OP_str == "是")
    {//这里暂时先设置密码和操作者代码为全0
        memset(pframe->data + data_len, 0x00, 8);
        data_len += 8;
    }

    //组建body域(如果该功能码需要body域的话)
    QString containBody = model_645_FuncCode->record(db_row).value("containBody").toString();
    if(containBody == "是")
    {
        QString custom_data = ui->lineEdit645CustomData->text();
        int custom_data_len = hex_str_to_byte_arr(custom_data, pframe->data + data_len, sizeof(frame645)-20, false);
        int body_len_theory = model_645_FuncCode->record(db_row).value("body_len").toInt(&isOK);
        if(!isOK)
        {
            //body长度不固定，输入多长就是多长
            data_len += custom_data_len;
        }
        else
        {
            //长度是固定的，但是gui上输入的body域的长度域与理论值不符
            if(body_len_theory != custom_data_len)
            {
                QMessageBox::information(this, tr("错误提示"), QString("645->body域应当输入%1个字节").arg(body_len_theory));
                return NULL;
            }
            data_len += custom_data_len;
        }
    }

    pframe->data_len = data_len;


//    QString did_str=ui->lineEdit645DID->text();
//    int did_len;
//    QString custom_data = ui->lineEdit645CustomData->text();
//    int custom_data_len=0;
//    switch(pframe->control_byte)
//    {
//        case WRITE_METER_ADDR:  //写设备地址
//                    custom_data_len = hex_str_to_byte_arr(custom_data, pframe->data, false);
//                    if(custom_data_len !=6 )
//                    {
//                        QMessageBox::information(this, tr("错误提示"), tr("07645的设备地址必须填入6个字节！"));
//                        return NULL;
//                    }
//                    pframe->data_len=6;
//                    break;
//        case READ_METER_ADDR://读设备地址
//                    pframe->data_len=0;
//                    break;
//        case WRITE_DATA:  //写数据
//                    did_len = hex_str_to_byte_arr(did_str, pframe->data, false);//DID
//                    if(did_len<0)
//                        return NULL;
//                    custom_data_len = hex_str_to_byte_arr(custom_data, ((data_645write *)pframe->data)->body, false);
//                    if(custom_data_len < 0 )
//                    {
//                        QMessageBox::information(this, tr("错误提示"), tr("07645的数据域内容有误！"));
//                        return NULL;
//                    }
//                    pframe->data_len=DID645_LEN+4+4+custom_data_len;
//                    break;
//        case READ_DATA://读数据
//                    did_len = hex_str_to_byte_arr(did_str, pframe->data, false);//DID
//                    if(did_len<0)
//                        return NULL;
//                    pframe->data_len=4;
//                    break;
//        case RADIATE_TIMING:  //广播校时
//                    custom_data_len = hex_str_to_byte_arr(custom_data, pframe->data, false);
//                    if(custom_data_len !=6 )
//                    {
//                        QMessageBox::information(this, tr("错误提示"), tr("645广播校时，数据域必须填入6个字节！"));
//                        return NULL;
//                    }
//                    pframe->data_len=6;
//                    break;
//        case METER_CLEAR://电表清0
//                    pframe->data_len=8;//密码和操作者随便填(这里不填了，保持上次的随机值)
//                    break;
//        default:    QMessageBox::information(this, tr("功能提示"),
//                    QString("目前尚不支持组建控制码为%1H的报文").arg(pframe->control_byte,2,16,QChar(' ')).toUpper());
//                    return NULL;

//    }




    //数据域+33H
    if(true == ui->checkBox33H->isChecked())
        memadd(pframe->data,0X33,pframe->data_len);

    //校验和与报文尾
    pframe->data[pframe->data_len] = checksum((uint8 *)pframe, FRAME_645_HEAD+pframe->data_len );
    pframe->data[pframe->data_len+1] = 0x16;

    //复制到7E的data域
    QByteArray ByteArray=QByteArray((const char *)pframe,FRAME_645_HEAD+pframe->data_len+2);//数组转QByteArray类
    QString frame_hex_str = byte_num_to_hex_string_with_space(ByteArray);
    ui->lineEditData->setText(frame_hex_str);
    return pframe;

}
void MainWindow::on_pushButtonScan_clicked()
{
    ui->comboBox_portName->clear();
    QTime current_time =QTime::currentTime();
    ui->textEditRece->append(QString("扫描时间:%1点%2分%3秒").arg(current_time.hour()).arg(current_time.minute()).arg(current_time.second())  );

    ui->textEditRece->append("共扫描到 "
                              + QString::number(QSerialPortInfo::availablePorts().count())
                              + " 个可用串口：");


    foreach( const QSerialPortInfo &Info,QSerialPortInfo::availablePorts())//读取串口信息
    {
        qDebug() << "portName    :"  << Info.portName();//调试时可以看的串口信息
        qDebug() << "Description   :" << Info.description();
        qDebug() << "Manufacturer:" << Info.manufacturer();

        QString space_str;
        space_str.fill(' ',10 - Info.portName().length());
        QString item_str = Info.portName() + space_str +  Info.description();

        ui->textEditRece->append(item_str);
        QSerialPort serial;
        serial.setPort(Info);
        ui->comboBox_portName->addItem(item_str );//在comboBox那添加串口号
    }
    ui->comboBox_portName->adjustItemWidth();

    ui->textEditRece->append("\r\n");
    QTextCursor cursor=ui->textEditRece->textCursor();//保持编辑器在光标最后一行
    cursor.movePosition(QTextCursor::End); //保持编辑器在光标最后一行
    ui->textEditRece->setTextCursor(cursor);//保持编辑器在光标最后一行
}

void MainWindow::on_pushButtonOpenSerial_clicked()
{
    QString port_name_str = ui->comboBox_portName->currentText();// =com3:silicon
    QRegExp rx("(com|COM)\\d{1,3}");
    rx.indexIn(port_name_str);
    port_name_str = rx.cap();

    myserial.setPortName(port_name_str);//combo中选定的串口号
    myserial.setBaudRate((ui->comboBoxBaud->currentText()).toInt());
    myserial.setDataBits((enum QSerialPort::DataBits)(ui->comboBoxDataBits->currentText().toInt()));

    QString StopBitsStr = ui->comboBoxStopBits->currentText();
    if(StopBitsStr == "1")
        myserial.setStopBits(QSerialPort::OneStop);
    else if(StopBitsStr == "1.5")
        myserial.setStopBits(QSerialPort::OneAndHalfStop);
    else if(StopBitsStr == "2")
        myserial.setStopBits(QSerialPort::TwoStop);
    else
        QMessageBox::information(this, tr("注意"), tr("停止位设置错误，程序可能无法正常运行"));


    QString ParityStr = ui->comboBoxParity->currentText();
    if(ParityStr == "无")
        myserial.setParity(QSerialPort::NoParity);
    else if(ParityStr == "奇校验")
        myserial.setParity(QSerialPort::OddParity);
    else if(ParityStr == "偶校验")
        myserial.setParity(QSerialPort::EvenParity);
    else
        QMessageBox::information(this, tr("注意"), tr("校验位设置错误，程序可能无法正常运行"));



    QString flowCtrlStr = ui->comboBoxFlow->currentText();
    if(flowCtrlStr == "无")
        myserial.setFlowControl(QSerialPort::NoFlowControl);
    else if(flowCtrlStr == "硬件流(CTS/RTS)")
        myserial.setFlowControl(QSerialPort::HardwareControl);
    else if(flowCtrlStr == "软件流(XON/XOFF)")
        myserial.setFlowControl(QSerialPort::SoftwareControl);
    else
        QMessageBox::information(this, tr("注意"), tr("流控制设置错误，程序可能无法正常运行"));


    if( myserial.isOpen() )
        myserial.close();
    else
        myserial.open(QIODevice::ReadWrite);//打开串口

    if( myserial.isOpen() )
    {
        ui->pushButtonOpenSerial->setIcon(QIcon(":/image/serial_opened_icon"));
        ui->pushButtonOpenSerial->setText("关闭串口");
    }
    else
    {
        ui->pushButtonOpenSerial->setIcon(QIcon(":/image/serial_closed_icon"));
        ui->pushButtonOpenSerial->setText("打开串口");
    }

    if(myserial.error())
    {
        //QMessageBox::information(this, tr("提示"), tr("操作失败，原因可能是:\r\n1、该串口正在被其他软件占用\r\n2、串口已被拔出\r\n请检查原因或者重启本软件！"));
        qDebug()<<"串口打开失败，原因是："<<myserial.error();
        myserial.clearError();
    }
}
void MainWindow::on_pushButtonAppendSum_clicked()
{
    uint8 cunstom_buf[280];

    int cunstom_len = hex_str_to_byte_arr(ui->lineEditCustomFrame->text(), cunstom_buf, sizeof(cunstom_buf), true);
    if(cunstom_len<0)
        QMessageBox::information(this, tr("错误"), tr("自组帧格式有误"));
    else
    {
        cunstom_buf[cunstom_len] = checksum(cunstom_buf, cunstom_len);
        QString str = byte_num_to_hex_string_with_space(cunstom_buf, cunstom_len+1);
        ui->lineEditCustomFrame->setText(str);
    }

}

void MainWindow::on_pushButtonSend_clicked()
{
    if(ui->checkBoxCustomFrame->isChecked())//自组帧
    {
        uint8 cunstom_buf[280];

        int cunstom_len = hex_str_to_byte_arr(ui->lineEditCustomFrame->text(), cunstom_buf, sizeof(cunstom_buf),true);
        if(cunstom_len<0)
            QMessageBox::information(this, tr("错误"), tr("自组帧格式有误"));
        else
        {
            if(ui->checkBoxCustomSum->isChecked())
            {
                cunstom_buf[cunstom_len] = checksum(cunstom_buf, cunstom_len);
                cunstom_len++;
            }
            my_serial_send((const char *)cunstom_buf, cunstom_len);
        }

    }
    else
    {
        if(Qt::Checked == ui->checkBoxUse645->checkState())
        {
            struct frame07645 *pframe645=encodeFrame645_FromGui();//组建645
            if(pframe645 == NULL)
            {
                QMessageBox::information(this, tr("错误提示"), tr("输入数据有误，无法组建645报文，请检查"));
                return;
            }
        }

        struct SHS_frame *pframe=encodeFrame7E_FromGui();//组建7E
        if(pframe == NULL)
        {
            QMessageBox::information(this, tr("错误提示"), tr("输入数据有误，无法组建7E报文，请检查"));
            return;
        }
        my_serial_send((const char *)pframe, SHS_FRAME_HEAD+pframe->length+1);
    }

}

void MainWindow::insert_frame_to_table(QString send_or_rece,QByteArray byte)
{
    int rowCount = ui->tableWidgetDisp->rowCount();
    ui->tableWidgetDisp->setRowCount(rowCount+1);
    rowCount = ui->tableWidgetDisp->rowCount();



    QString frameStr = byte_num_to_hex_string_with_space(byte);
    QString timeStr = QString("%1:%2:%3.%4")
            .arg(QTime::currentTime().hour())
            .arg(QTime::currentTime().minute(),2,10,(QChar)'0')
            .arg(QTime::currentTime().second(),2,10,(QChar)'0')
            .arg(QTime::currentTime().msec(),2,10,(QChar)'0');


    ui->tableWidgetDisp->setItem(rowCount-1, 0, new QTableWidgetItem(timeStr));//插入时间
    ui->tableWidgetDisp->setItem(rowCount-1, 1, new QTableWidgetItem(send_or_rece));//插入方向
    ui->tableWidgetDisp->setItem(rowCount-1, 2, new QTableWidgetItem(frameStr));//插入报文

    if(send_or_rece == "已发送")
    {
        ui->tableWidgetDisp->item(rowCount-1, 1)->setForeground(QColor(0, 0, 140));//rgb(211, 230, 255)
        ui->tableWidgetDisp->item(rowCount-1, 2)->setForeground(QColor(0, 0, 140));//成功发送的报文:蓝色
    }
    else if(send_or_rece == "接收")
    {
        ui->tableWidgetDisp->item(rowCount-1, 1)->setForeground(QColor(140,0,0));
        ui->tableWidgetDisp->item(rowCount-1, 2)->setForeground(QColor(140,0,0));//接收的报文：红色
    }



    ui->tableWidgetDisp->setCurrentCell(rowCount-1,2,QItemSelectionModel::Deselect);//设置current行，但不选中


}
void MainWindow::my_serial_send(const char *data, qint64 len)
{
    QByteArray byte = QByteArray(data,len);//数组转QByteArray类
    if( myserial.isOpen() )
    {
//        int SendCnt = ui->labelSendCnt->text().toInt() + 1;
//        ui->labelSendCnt->setText(QString::number(SendCnt));

        int SendCnt = ui->labelSendCnt->text().toInt() + 1;
        int ReceCnt = ui->labelReceCnt->text().toInt();
        ui->labelSendCnt->setText(QString::number(SendCnt));
        int SuccessRate = 100 * (float)ReceCnt / (SendCnt-1>0?SendCnt-1:1);
        SuccessRate = SuccessRate>100? 100: SuccessRate;
        ui->labelSuccessRate->setText(QString("%1%").arg(SuccessRate));

        insert_frame_to_table("已发送",byte);
        myserial.write(data,len);
    }
    else
    {
        insert_frame_to_table("串口未开，发送失败",byte);
        ui->textEditRece->append("串口未打开！");
        QMessageBox::information(this, tr("提示"), tr("串口未打开！"));
    }

}
void MainWindow::on_pushButton_clicked()
{
    ui->labelReceByteCnt->setText("0");
    ui->textEditRece->setText("");

}

void MainWindow::on_timer_serial_rece_timeout()
{
    timer_serial_rece_timeout->stop();

    insert_frame_to_table("接收",rece_frame);
    rece_frame.clear();

    ui->tableWidgetDisp->setCurrentCell(ui->tableWidgetDisp->rowCount()-1,2,QItemSelectionModel::Deselect);//设置current行，但不选中
    on_tableWidgetDisp_clicked(ui->tableWidgetDisp->currentIndex());//自动选中最后一行，以便立即解析
    qDebug()<<"断帧，求成功率";

    int SendCnt = ui->labelSendCnt->text().toInt();
    int ReceCnt = ui->labelReceCnt->text().toInt() + 1;
    ui->labelReceCnt->setText(QString::number(ReceCnt));

    int SuccessRate = 100 * (float)ReceCnt / (SendCnt>0?SendCnt:1);
    SuccessRate = SuccessRate>100? 100: SuccessRate;
    ui->labelSuccessRate->setText(QString("%1%").arg(SuccessRate));
}



void MainWindow::on_pushButtonClrTable_clicked()
{
//    int rowCount=ui->tableWidgetDisp->rowCount();
//    while(rowCount)
//        ui->tableWidgetDisp->removeRow((rowCount--)-1);


    //ui->tableWidgetDisp->clearContents();
    ui->tableWidgetDisp->setRowCount(0);

    ui->labelReceCnt->setText("0");
    ui->labelSendCnt->setText("0");
}



void MainWindow::on_pushButtonSelectDid_clicked()
{
    dlg_select_did->show();
}

void MainWindow::on_DidSelect(QString str)
{
    qDebug()<<"选择的645->DID是:"+str;
    ui->lineEdit645DID->setText(str);
}
void MainWindow::on_7E_DidSelect(QString str)
{
    qDebug()<<"选择的7E->DID是:"+str;
    ui->lineEditDID->setText(str);
}
void MainWindow::on_ReceSettings(struct Settings set)
{
    qDebug()<<"收到的键:"<<set.key_showDidAnaly;
    memcpy(&this->settings, &set, sizeof(struct Settings));
    qDebug()<<"复制后的键:"<<this->settings.key_showDidAnaly;

}
void MainWindow::on_loacl_frame_encoded(const char *data, qint64 len)
{    
    //insert_frame_to_table("发送",byte);
    qDebug()<<"窗口间本地报文传送成功";
    my_serial_send(data,len);
}
void MainWindow::on_lineEditDID_editingFinished()
{
    //char did_byte;
    bool ok1,ok2;
    ui->lineEditDID->text().right(2).toInt(&ok1, 16);
    ui->lineEditDID->text().left(2).toInt(&ok2, 16);
    if(ok1 && ok2)
    {}
    else
    {
        ui->lineEditDID->setText("0001");
        QMessageBox::information(this, tr("错误提示"), QString("填入的7E->did域内容格式有误！可能的原因：\r\n")
                                                    + QString("包含了非法(非HEX)字符"));
    }

}


void MainWindow::on_lineEditTaid_textChanged(const QString &arg1)
{

    if(ui->lineEditTaid->text() != "FFFFFFFF")
        ui->lineEditMeterAddr->setText(arg1);
    else
    {
        if(ui->comboBoxCMD645->currentText().contains("广播校时"))
            ui->lineEditMeterAddr->setText("999999999999");
        else
            ui->lineEditMeterAddr->setText("AAAAAAAAAAAA");
    }
}



void MainWindow::on_comboBoxCMD645_currentIndexChanged(const QString &arg1)
{
    bool isOK;
    uint8 ctrl_byte = arg1.left(2).toInt(&isOK, 16);
    if(    (ctrl_byte == RADIATE_TIMING )//广播校时
        || (ctrl_byte == READ_METER_ADDR )//读通信地址
        || (ctrl_byte == WRITE_METER_ADDR )//, "写通信地址"
    )
    {//这几个控制字自动修改地址为广播地址99或AA
        ui->checkBoxBroadcast->setChecked(false);
        ui->checkBoxBroadcast->setChecked(true);//触发一次checkBoxBroadcast的statechanged
    }
    else
    {
        ui->checkBoxBroadcast->setChecked(true);
        ui->checkBoxBroadcast->setChecked(false);//触发一次checkBoxBroadcast的statechanged
    }


    int db_row=0; //首先根据功能码定位到数据库表指定的行
    for(db_row=0;db_row < model_645_FuncCode->rowCount();db_row++)
    {
        QString dbFuncCode_str = model_645_FuncCode->record(db_row).value("funcCode").toString().left(2);
        uint8 dbFuncCode = dbFuncCode_str.toInt(&isOK,16);
        if( (ctrl_byte & 0x1F) ==  dbFuncCode)
        {
            break;
        }
    }
    if(db_row == model_645_FuncCode->rowCount() )
    {
        QMessageBox::information(this, tr("功能提示"), "目前不支持该功能码，在功能码维护界面添加后即可支持");
        return;
    }
    //在数据库中查找功能码已完成


    QString containBody = model_645_FuncCode->record(db_row).value("containBody").toString();
    ui->lineEdit645CustomData->setEnabled(true);
    if(containBody == "是")
        ui->lineEdit645CustomData->setHidden(false);//显示body输入域
    else
        ui->lineEdit645CustomData->setHidden(true);//隐藏body输入域

    QString containDid = model_645_FuncCode->record(db_row).value("containDid").toString();
    if(containDid == "是")
        ui->lineEdit645DID->setHidden(false);//显示did输入域
    else
        ui->lineEdit645DID->setHidden(true);//隐藏did输入域
}



void MainWindow::on_checkBoxBroadcast_stateChanged(int arg1)
{
    int check_state = arg1;
    static QString taid_history_str = ui->lineEditTaid->text();

    if(Qt::Checked == check_state)
    {
        ui->label_Taid->setText("TAID(HEX)");
        taid_history_str = ui->lineEditTaid->text();
        ui->lineEditTaid->setText("FFFFFFFF");
        ui->lineEditTaid->setEnabled(false);

//        ui->comboBoxGidType->setHidden(false);
//        ui->lineEditSid->setHidden(false);
//        ui->labelGidType->setHidden(false);
//        ui->labelSid->setHidden(false);
    }
    else
    {
        ui->label_Taid->setText("TAID(10进制)");
        ui->lineEditTaid->setText(taid_history_str);
        ui->lineEditTaid->setEnabled(true);

    }



    static QPropertyAnimation animation(ui->frameBoxGroup, "maximumSize");
    animation.setDuration(150);

    QSize frame_size = ui->frameBoxGroup->maximumSize();

    if(check_state == Qt::Checked)
    {
        frame_size.setWidth(140);
        animation.setEndValue(frame_size);
        animation.start();

    }
    else
    {
        frame_size.setWidth(0);
        animation.setEndValue(frame_size);
        animation.start();
    }
}

/*
<style>
f1{color: red;} //定义一个局部格式，名称为f1
f2{color: red;} //定义一个局部格式，名称为f2
</style>
<p>这是一段的测试的文字，中间需要一个字是<content>红</content>色的突出显示</p> //在某长段中使用局部格式
*/
void MainWindow::on_tableWidgetDisp_clicked(const QModelIndex &index)
{
    int Row = index.row();
    QString str = ui->tableWidgetDisp->item(Row,2)->text();
    uint8 frame_buf[300];
    qDebug("选中了第%d行", Row);
    qDebug()<<"报文内容是 "<<str;
    int total_len = hex_str_to_byte_arr(str, frame_buf, sizeof(frame_buf),true);
    if(total_len<0)
    {
        if(-1 == total_len) ui->textEditAnalysis->setText("报文解析失败：\n每一个字节必须用两个Hex字符表示");
        if(-2 == total_len) ui->textEditAnalysis->setText("报文解析失败：\n报文中含有非hex字符！");
        if(-3 == total_len) ui->textEditAnalysis->setText("报文解析失败：\n报文过长，可能是设置的断帧时长过短，导致帧与帧之间没有断开！");
        return;
    }


    struct SHS_frame *pframe = get_smart_frame(frame_buf, total_len);

    QString ResultStr;
    QString AnalyStr;


    ResultStr += QString("<style>p{margin:0 0 0 0} gy{color: #8E8E8E;}</style>");//设置段间距，上右下左，自定义灰色

    ResultStr += QString("<p>时间:<gy>%1点%2分%3秒</gy></p>")
                .arg(QTime::currentTime().hour())
                .arg(QTime::currentTime().minute())
                .arg(QTime::currentTime().second());

    if(pframe!=NULL)// 是7E
    {
        ResultStr += "<p>报文类型：<gy>是7E</gy></p>";
        analysis_7E_frame(pframe, AnalyStr);
    }
    else
    {
        ResultStr += "<p>报文类型：<gy>不是7E，";
        struct frame07645 *pframe645 = get_645_frame(frame_buf, total_len);
        if(pframe645 != NULL)//是645
        {
            ResultStr += "是645\r\n</gy></p>";
            analysis_07645_frame(pframe645, AnalyStr);
        }
        else
        {
            ResultStr += "不是645</gy></p>";
        }
    }


    ui->textEditAnalysis->setHtml(ResultStr + AnalyStr);
}

void MainWindow::on_pushButtonSetSaid_clicked()
{
    uint8 frame_set_aid[]={ 0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x01,0x01,0x00,0x00,0x00,0x85};
    int total_len=sizeof(frame_set_aid);
    struct SHS_local_frame *pframe = (struct SHS_local_frame *)frame_set_aid;
    //*(quint32 *)(pframe->data) = ui->lineEditSaid->text().toInt();
    put_le_val(ui->lineEditSaid->text().toInt(), pframe->data, 4);
    frame_set_aid[total_len-1] = checksum(frame_set_aid,total_len-1);    

    my_serial_send((const char *)frame_set_aid,total_len);
}


void MainWindow::on_pushButtonSetPanid_clicked()
{
    uint8 frame_set_panid[]={ 0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0C,0x00,0x00,0x8D};
    int total_len=sizeof(frame_set_panid);
    struct SHS_local_frame *pframe = (struct SHS_local_frame *)frame_set_panid;
    //*(quint16 *)(pframe->data) = ui->lineEditPanid->text().toInt();
    put_le_val(ui->lineEditPanid->text().toInt(), pframe->data, 2);
    frame_set_panid[total_len-1] = checksum(frame_set_panid,total_len-1);
    my_serial_send((const char *)frame_set_panid,total_len);

}
void MainWindow::on_pushButtonSetbaud_clicked()
{
    uint8 frame_set_band[]={0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x08,0x02,0x00,0x8B};
    int total_len=sizeof(frame_set_band);
    struct SHS_local_frame *pframe = (struct SHS_local_frame *)frame_set_band;
    quint32 band = ui->comboBoxLocalBand->currentText().toInt();
    switch(band)
    {
        case 2400: pframe->data[0] = 0;break;
        case 4800: pframe->data[0] = 1;break;
        case 9600: pframe->data[0] = 2;break;
        case 57600: pframe->data[0] = 3;break;
        case 115200: pframe->data[0] = 4;break;
        default: pframe->data[0] = 2;
    }

    pframe->data[1] = 0;//0x00-无校验，0x01-偶校验，0x02-奇校验
    pframe->length = 1 +2;//did+data
    frame_set_band[total_len-1] = checksum(frame_set_band,total_len-1);

    my_serial_send((const char *)frame_set_band,total_len);


}
void MainWindow::on_pushButtonGetPlcVer_clicked()
{
    uint8 frame_GetPlcVer[]={0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0E,0x8D};
    int total_len=sizeof(frame_GetPlcVer);
    my_serial_send((const char *)frame_GetPlcVer,total_len);
}



void MainWindow::on_pushButtonSelect7EDid_clicked()
{
    dlg_select_7E_did->exec();
}


void MainWindow::on_checkBoxUse645_stateChanged(int arg1)
{
    //QPropertyAnimation *animation = new QPropertyAnimation(ui->frameBox645, "maximumSize");
    static QPropertyAnimation animation(ui->frameBox645, "maximumHeight");
    animation.setDuration(150);


    if(arg1 == Qt::Unchecked)
    {
        ui->lineEditDID->setText("0001");
        ui->lineEditDID->setEnabled(true);
        ui->pushButtonSelect7EDid->setEnabled(true);
        //ui->spinBoxMultiDid->setValue(1);
        ui->spinBoxMultiDid->setEnabled(true);

        animation.setEndValue(0);
        animation.start();

    }
    else
    {
        ui->lineEditDID->setText("0B02");        
        ui->lineEditDID->setEnabled(false);
        ui->pushButtonSelect7EDid->setEnabled(false);
        ui->spinBoxMultiDid->setValue(1);
        ui->spinBoxMultiDid->setEnabled(false);

        animation.setEndValue(74);
        animation.start();
    }


}




void MainWindow::on_pushButtonPreserveRemoteCMD_clicked()
{
    dlg_preserve_remote_cmd->exec();
}

void MainWindow::on_checkBoxCustomFrame_stateChanged(int arg1)
{

    if(arg1 == Qt::Checked)
    {
        ui->lineEditCustomFrame->setEnabled(true);
        ui->frameBox7E->setEnabled(false);
        ui->frameBox645->setEnabled(false);
    }
    else
    {
        ui->lineEditCustomFrame->setEnabled(false);
        ui->frameBox7E->setEnabled(true);
        ui->frameBox645->setEnabled(true);
    }

}

void MainWindow::on_pushButtonSend645_clicked()
{
    struct frame07645 *pframe645=encodeFrame645_FromGui();//组建645
    if(pframe645 == NULL)
    {
        QMessageBox::information(this, tr("错误提示"), QString("输入数据有误，无法组建645报文\r\n")
                                 +QString("注意：单个字节也要输入两个hex字符，如0x05，应输入05，而不能只输入5"));
        return;
    }
    int frame645_total_len = 10+pframe645->data_len+2;

    my_serial_send((const char *)pframe645,frame645_total_len);
}

void MainWindow::on_pushButton_2_clicked()
{
    //QMessageBox::information(this, tr("提示"), tr("这个功能还没做"));
    //dlg_preserve_local_cmd->exec();
    dlg_preserve_local_cmd->show();

}



void MainWindow::on_spinBoxFrameInterval_editingFinished()
{
    frameInterTime = ui->spinBoxFrameInterval->value();
    //QMessageBox::information(this, tr("提示"), QString("断帧时长设置成功"));
    ui->textEditRece->append("超过"
                             +ui->spinBoxFrameInterval->text()
                             +"ms仍接收不到下一字节，则认为一帧数据接收完毕");
}

void MainWindow::on_pushButtonFuncCode_clicked()
{
    dlg_preserve_645_func_code->exec();
}


void MainWindow::on_timer_recycle_send_timeout()
{
    timer_serial_rece_timeout->stop();
    if((ui->checkBoxRecycle->isChecked() == true)
       && (myserial.isOpen()) )
    {
        on_pushButtonSend_clicked();
        int recycle_time = ui->spinBoxRecycle->value();        
        timer_recycle_send->start(recycle_time);
        qDebug()<<"循环发送报文";

    }
    else
    {
        timer_recycle_send->stop();
        qDebug()<<"定时器停止";
    }

}

void MainWindow::on_checkBoxRecycle_stateChanged(int arg1)
{
    uint8 cunstom_buf[280];

    if( !myserial.isOpen() )
    {
        QMessageBox::information(this, tr("提示"), QString("串口未开。\r\n请先点击单次发送，确认报文无误后，再启动循环发送"));
        timer_recycle_send->stop();
        return;
    }

    if( (arg1 == Qt::Checked) )
    {
        int cunstom_len = hex_str_to_byte_arr(ui->lineEditCustomFrame->text(), cunstom_buf,sizeof(cunstom_buf), true);
        if(cunstom_len<=0)
        {
            timer_serial_rece_timeout->stop();
            QMessageBox::information(this, tr("错误"), tr("自组帧格式有误，循环停止"));
            return;
        }

        qDebug()<<"启动定时器";
        int recycle_time = ui->spinBoxRecycle->value();
        timer_recycle_send->start(recycle_time);
        ui->spinBoxRecycle->setEnabled(false);// 禁止修改循环时间
    }
    else
    {
        timer_recycle_send->stop();
        ui->spinBoxRecycle->setEnabled(true);// 允许修改循环时间
        qDebug()<<"关闭定时器";
    }
}


void MainWindow::on_checkBoxFoldLocal_stateChanged(int arg1)
{
    //qDebug()<<"本地折叠";
    static QPropertyAnimation animation(ui->frameBoxLocal, "maximumSize");
    animation.setDuration(150);

    QSize frame_size = ui->frameBoxLocal->maximumSize();

    if(arg1 == Qt::Checked)
    {
        frame_size.setHeight(0);
        animation.setEndValue(frame_size);
        animation.start();

    }
    else
    {
        frame_size.setHeight(138);
        animation.setEndValue(frame_size);
        animation.start();
    }
}

void MainWindow::on_pushButtonAddToTest_clicked()
{

    bool isOK;
    QString description = QInputDialog::getText(NULL, "输入",
                                               "请输入该测试用例的描述信息",
                                               QLineEdit::Normal,
                                               "描述信息",
                                               &isOK);
    if(!isOK) return;


    QSqlRecord new_record = model_common_7E->record();//读入空行，载入该表的列名信息

    QString cur_cmd_str = ui->comboBoxCMD->currentText();
    QString cur_did_str = ui->lineEditDID->text();
    QString cur_data_str = ui->lineEditData->text();
    //new_record.setValue("key",NULL);//0代表自增
    new_record.setValue("instruction",description);
    new_record.setValue("cmd",cur_cmd_str);
    new_record.setValue("did",cur_did_str);
    new_record.setValue("data",cur_data_str);


    if(model_common_7E->insertRecord(-1,new_record))//-1代表自动追加新行
    {
        qDebug()<<"insertRecord成功";
    }
    else
    {
        qDebug()<<"insertRecord失败:"<<model_common_7E->lastError().text();
    }


    if(model_common_7E->submitAll())
    {
        qDebug()<<"提交成功";
        model_common_7E->select();
    }
    else
    {
        qDebug()<<"提交失败:"<<model_common_7E->lastError().text();
    }



    ui->comboBoxTestExample->addItem(description);

//    QString cmd_str = record.value("cmd").toString();
//    QString instruction_str = record.value("instruction").toString();
}

void MainWindow::on_pushButtonPreserveTest_clicked()
{
    dlg_common_frame->show();
}

void MainWindow::on_comboBoxTestExample_currentIndexChanged(const QString &arg1)
{

    int db_row=0;//先定位到db中某功能码所在的行

    for(db_row=0;db_row < model_common_7E->rowCount();db_row++)
    {
        QString instruction_str = model_common_7E->record(db_row).value("instruction").toString();

        if( arg1 == instruction_str )
        {
            break;
        }
    }

    if(db_row == model_common_7E->rowCount())
    {
        QMessageBox::information(this, tr("提示"), QString("该测试用例已被删除或修改，请重启软件试试"));
        return;
    }

    QSqlRecord specified_record = model_common_7E->record(db_row);

    ui->comboBoxCMD->setCurrentText(specified_record.value("cmd").toString());
    ui->lineEditDID->setText(specified_record.value("did").toString());
    ui->lineEditData->setText(specified_record.value("data").toString());

}

void MainWindow::on_checkBoxFoldSerial_stateChanged(int arg1)
{
    static QPropertyAnimation animation(ui->frameBoxSerial, "maximumSize");
    animation.setDuration(150);

    QSize frame_size = ui->frameBoxSerial->maximumSize();

    if(arg1 == Qt::Checked)
    {
        frame_size.setHeight(0);
        animation.setEndValue(frame_size);
        animation.start();

    }
    else
    {
        frame_size.setHeight(163);
        animation.setEndValue(frame_size);
        animation.start();
    }
}



void MainWindow::on_spinBoxMultiDid_valueChanged(int arg1)
{

    uint did_cnt = arg1;//did_cnt=3则显示出frameBoxBody[0]~frameBoxBody[did_cnt-1]，其余隐藏

    const int total_cnt = sizeof(frameBoxBody)/sizeof(frameBoxBody[0]);


    uint idx;
    for( idx=0; idx<total_cnt; idx++ )//从idx = 0-> did_cnt-1，显示出来
    {
        if(idx<did_cnt-1)
        {
            frameBoxBody[idx]->hideWithAnimate(false);
        }
        else
        {
           frameBoxBody[idx]->hideWithAnimate(true);
        }


    }
}



void MainWindow::on_lineEditDID_customContextMenuRequested(const QPoint &pos)
{
    qDebug()<<"右键"<<pos;
}

void MainWindow::keyPressEvent(QKeyEvent *event)
{
    //qDebug()<<"按下,setting。key="<<settings.key_showDidAnaly;
    if( ((0 == settings.key_showDidAnaly) && (event->key() == Qt::Key_Control))
         || ((1 == settings.key_showDidAnaly) && (event->key() == Qt::Key_Shift))
     )
     {

         ui->lineEditDID->showDidAnalysis(true);
         for(uint8 idx=0;idx<sizeof(frameBoxBody)/sizeof(frameBoxBody[0]);idx++ )
         {
             if(frameBoxBody[idx]->maximumWidth() != 0)//没有被折叠的，则显示tip
                frameBoxBody[idx]->lineEditDID->showDidAnalysis(true);
             else
                frameBoxBody[idx]->lineEditDID->showDidAnalysis(false);

         }

         if(ui->frameBox645->maximumHeight() != 0)
            ui->lineEdit645DID->showDidAnalysis(true);
     }

}
void MainWindow::keyReleaseEvent(QKeyEvent *event)
{
    if( ((0 == settings.key_showDidAnaly) && (event->key() == Qt::Key_Control))
        || ((1 == settings.key_showDidAnaly) && (event->key() == Qt::Key_Shift))
    )
    {
        //qDebug()<<"SHIFT 弹起";
        ui->lineEditDID->showDidAnalysis(false);
        for(uint8 idx=0;idx<sizeof(frameBoxBody)/sizeof(frameBoxBody[0]);idx++ )
        {
            frameBoxBody[idx]->lineEditDID->showDidAnalysis(false);
        }
        ui->lineEdit645DID->showDidAnalysis(false);
    }


}
void MainWindow::resizeEvent(QResizeEvent *event)
{
    event->accept();
//    //qDebug()<<"resize";

//    if(ui->SliderSetWidth->hasFocus())
//        return;

//    int textAnalyWidth = ui->textEditAnalysis->width();
//    qDebug()<<"宽度变为:"<<textAnalyWidth;
//    ui->SliderSetWidth->setValue(textAnalyWidth - ui->textEditAnalysis->minimumWidth());

}
void MainWindow::focusInEvent(QFocusEvent *event)
{

    event->accept();
    qDebug()<<"焦点";
}
void MainWindow::on_pushButtonClearCustomFrame_clicked()
{
    ui->lineEditCustomFrame->setText("");
}

void MainWindow::on_SliderSetWidth_valueChanged(int value)
{
    //qDebug()<<"是否有焦点"<<ui->SliderSetWidth->hasFocus();

    if(false == ui->SliderSetWidth->hasFocus())
        return;
    ui->textEditAnalysis->setMaximumWidth(178+value);
    ui->textEditAnalysis->setMinimumWidth(178+value);//大小同时改，才能把窗口强制变大

    int slider_Y = ui->SliderSetWidth->geometry().y();
    int frame7E_X = ui->frameBox7E->geometry().x();
    int pic_height = ui->frameBoxSerialBig->geometry().height()+ui->frameBoxLocalBig->geometry().height();
    if(pic_height>=150) pic_height=150;
    int pic_width = pic_height*0.8;

    labelPush->setGeometry(frame7E_X-pic_width, slider_Y-pic_height, pic_width ,pic_height);

    labelPush->show();

}

void MainWindow::on_SliderSetWidth_sliderReleased()
{
    ui->textEditAnalysis->setMinimumWidth(178);//最小值改回来，否则缩放窗口，窗口变不小
    labelPush->hide();
}
